package org.example.leetcode.codeExecutor.service;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.text.StrSubstitutor;
import org.example.leetcode.codeExecutor.utils.MinIOUtils;
import org.example.leetcode.codeExecutor.utils.SSHTools;
import org.example.leetcode.common._enum.CodeStatusEnum;
import org.example.leetcode.common._enum.IsTestOrSubmit;
import org.example.leetcode.common._enum.LanguageEnum;
import org.example.leetcode.common.domain.CodeDO;
import org.example.leetcode.common.domain.CodeResult;
import org.example.leetcode.common.utils.TitleUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.util.StopWatch;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.FutureTask;

@Component
@Slf4j
public abstract class CodeExecuteFlow {
    @Resource
            private ThreadPoolTaskExecutor myThreadPool;

    /**
     * 编译用户代码，并得到结果
     * @param codeDO 用户代码
     * @param codeFile 用户代码minio文件信息
     * @param codeResult 返回结果，回显对象
     * @return
     */
    String compileFileAndExecute(CodeDO codeDO, MinIOUtils.FileDto codeFile, CodeResult codeResult){
        StopWatch stopWatch = new StopWatch();
        stopWatch.start("下载代码");
//        String imageName=codeDO.getTitle()+codeDO.getPhone()+codeDO.getLanguage().toLowerCase();
        String execFile=codeFile.getFileName().substring(0,codeFile.getFileName().lastIndexOf("."));
        String testFile=null;
        try {
            //远程服务器下载代码文件到指定路径
            SSHTools.downloadCodeFile(MinIOUtils.LEETCODE,codeDO.getPhone(),codeDO.getLanguage(),codeFile.getFileName());
            stopWatch.stop();
            stopWatch.start("编译代码");
            //初步编译用户代码
            try{
//                String compile = SSHTools.compile(getLanguageEnum().getCompileCode(), codeFile.getFileName(),getLanguageEnum().getCode());
                  String compile=compile(codeFile.getFileName());
                if(!StringUtils.isEmpty(compile)){
                    codeResult.setStatus(CodeStatusEnum.compile_error.getCode());
                    compile=compile.replaceAll(codeFile.getFileName(),"");
                    codeResult.setMessage(compile);
                    return null;
                }
            }catch (Exception e){
                codeResult.setStatus(CodeStatusEnum.compile_error.getCode());
                codeResult.setMessage(CodeStatusEnum.compile_error.getName());
                return null;
            }
            stopWatch.stop();
            stopWatch.start("构建镜像");
            //构建镜像
//            SSHTools.SSHResult buildResult = SSHTools.buildDockerImage(imageName, codeFile.getFileName(), getLanguageEnum());
            stopWatch.stop();
            stopWatch.start("获取测试用例");
            //获取测试用例文件,需要管理员自行放置在minio中
            testFile = IsTestOrSubmit.SUBMIT.getCode().equals(codeDO.getIsTestOrSubmit())
                    ?SSHTools.getTestFile(codeDO.getTitle())
                    :getMyTestFile(codeDO);
            stopWatch.stop();
            stopWatch.start("执行代码");
            //docker执行代码
//            String res=SSHTools.runDocker(imageName, testFile,codeResult);
            String res=executeCode(execFile,testFile,codeResult);
            stopWatch.stop();
            System.out.println(stopWatch.prettyPrint());
            return res;
        }catch (Exception e){
            log.error(e.getMessage());
            codeResult.setStatus(CodeStatusEnum.error.getCode());
            codeResult.setMessage(e.getMessage());
        }
        finally {
            CountDownLatch countDownLatch=new CountDownLatch(1);
            String finalTestFile = testFile;
            myThreadPool.execute(()->{
                //String imageN=imageName;
                MinIOUtils.FileDto codeF=new MinIOUtils.FileDto();
                BeanUtils.copyProperties(codeFile,codeF);
                String test=finalTestFile.substring(finalTestFile.lastIndexOf("/")+1);
                countDownLatch.countDown();
                //删除容器
//                SSHTools.deleteContainerByImageName(imageN);
                //删除无用容器
//                SSHTools.deleteUnusedContainer();
//                SSHTools.deleteNoneImages();
                //删除镜像
//                SSHTools.deleteImages(imageN);
                //删除临时文件
                SSHTools.deleteFile(getLanguageEnum().getTargetUrl()+"/"+codeF.getFileName());
                SSHTools.deleteFile(finalTestFile);
                try {
                    //删除minio的文件
                    MinIOUtils.removeFile(MinIOUtils.LEETCODE,codeF.getObjectName());
                    MinIOUtils.removeFile(MinIOUtils.LEETCODE,"/temp/"+test);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            });
            //让主线程等待，直到线程池里的参数赋值完毕
            try {
                countDownLatch.await();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        return null;
    }

    /**
     * 将用户代码暂存入minio
     * @param codeDO
     * @return
     */
    MinIOUtils.FileDto createCodeFile(CodeDO codeDO){
        File file = null;
        try {
            file = File.createTempFile("temp", getLanguageEnum().getSuffix());
            String codeText = codeDO.getCodeText();
            OutputStream outputStream = new FileOutputStream(file);
            BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream,"UTF-8"));
            bufferedWriter.write(codeText);
            bufferedWriter.close();
            outputStream.close();
            //生成带minio地址
            String objectName=generateFileName(codeDO);
            MinIOUtils.uploadFile(MinIOUtils.LEETCODE,objectName,file.getAbsolutePath());
            MinIOUtils.FileDto fileDto = new MinIOUtils.FileDto();
            fileDto.setFileName(MinIOUtils.getObjectFileName(objectName));
            fileDto.setObjectName(objectName);
            return fileDto;
        } catch (Exception e) {
            log.error("代码写入文件失败");
            throw new RuntimeException(e);
        }finally {
            if(file.exists()){
                file.delete();
            }
        }
    }
    public abstract String getSuffix();

    /**
     * 获取自定义测试用例
     * @param codeDO
     * @return
     */
    String getMyTestFile(CodeDO codeDO){
        File file=null;
        try {
            file=File.createTempFile("temp",getSuffix());
            BufferedWriter bufferedWriter=new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file),"UTF-8"));
            bufferedWriter.write(codeDO.getTestData());
            bufferedWriter.close();
            String objectName="/temp/"+codeDO.getTitle()+codeDO.getLanguage()+codeDO.getPhone()+System.currentTimeMillis()+".txt";
            MinIOUtils.uploadFile(MinIOUtils.LEETCODE,objectName,file.getAbsolutePath());
            SSHTools.downloadFile(MinIOUtils.getBasisUrl()+objectName.substring(1),SSHTools.BASE_URL+objectName.substring(1));
            return SSHTools.TEMP_URL+"/"+objectName.substring(objectName.lastIndexOf("/")+1);
        } catch (Exception e) {
            log.error("获取自定义测试用例错误");
            throw new RuntimeException(e);
        }
        finally {
            if(file.exists()){
                file.delete();
            }
        }
    }
    protected String generateFileName(CodeDO codeDO){
        if(codeDO.getLanguage().equals(LanguageEnum.JAVA.getCode())){
            return codeDO.getPhone()+"/"+codeDO.getLanguage()+"/"+ TitleUtils.getValidTitle(codeDO.getTitle())+codeDO.getPhone()+getSuffix();
        }
        return codeDO.getPhone()+"/"+codeDO.getLanguage()+"/"+codeDO.getTitle()+System.currentTimeMillis()+getSuffix();
    }
    public abstract LanguageEnum getLanguageEnum();

    /**
     * 编译代码
     * @param fileUrl
     * @return
     */
    protected abstract String compile(String fileUrl);
    protected abstract String getExecuteCmd(String execFile,String testFile);
    protected abstract void getDeleteCmd(String execFile);

    /**
     * 运行代码
     * @param execFile
     * @param testFile
     * @return
     */
    protected  String executeCode(String execFile,String testFile,CodeResult codeResult){
        String cmd = getExecuteCmd(execFile,testFile);
        StopWatch stopWatch = new StopWatch();
        Callable callable = () -> {
            SSHTools sshTools = SSHTools.getSSHTools();
            stopWatch.start();
            SSHTools.SSHResult sshResult = sshTools.executeShell(cmd);
            stopWatch.stop();
            log.info("代码执行{}",stopWatch.getTotalTimeMillis());
            return sshResult.getMessage();
        };
        FutureTask task=new FutureTask(callable);
        Thread thread = new Thread(task);
        thread.start();
        //todo 执行时间
        try {
            Thread.sleep(2000);

        String res=null;
        if(!thread.isAlive()){
            res= (String) task.get();
            codeResult.setExecuteTime(stopWatch.getTotalTimeMillis());
        }
        else{
            thread.interrupt();
            codeResult.setStatus(CodeStatusEnum.out_of_time.getCode());
        }
        return res;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        finally {
            myThreadPool.execute(()->{
                getDeleteCmd(execFile);
            });
        }
    }


}
